import Bullet from "./Bullet";
import { ItemData, PlayerData, 头盔, 防弹衣 } from "./Datas/Data";
import { AStarManager, AStarNode } from "./Framework/Algorithm/AStar";
import { Constant, ItemType } from "./Framework/Const/Constant";
import AudioManager from "./Framework/Managers/AudioManager";
import { BundleManager, Bundles } from "./Framework/Managers/BundleManager";
import { DataManager } from "./Framework/Managers/DataManager";
import { EventManager, MyEvent } from "./Framework/Managers/EventManager";
import { PoolManager } from "./Framework/Managers/PoolManager";
import NodeUtil from "./Framework/Utils/NodeUtil";
import { ResourceUtil } from "./Framework/Utils/ResourceUtil";
import { ZTool } from "./Framework/Utils/ZTool";
import GameManager, { GameMode } from "./GameManager";
import { AniState } from "./PlayerController";
import Supplies from "./Supplies";
import GamePanel from "./UI/GamePanel";
import HPBar from "./UI/HPBar";

const { ccclass, property } = cc._decorator;


@ccclass
export default class EnemyController extends cc.Component {
    MAXHP = 200;

    private _hp = 0;
    public get HP(): number {
        return this._hp;
    }
    public set HP(value: number) {
        if (this.isDie) return;

        if (value <= 0) {
            this.isDie = true;
            const items = this.playerData.ItemToArray();
            this.playerData.Clear();
            this.RefreshEquip();
            this.PlayAni(AniState.Die);
            DataManager.EXP += 100;

            if (this.isTeamMode && this.isRedTeam) {
                GamePanel.Instance.RefreshTeamScore(0, 1);
            }
            if (this.isTeamMode && !this.isRedTeam) {
                GamePanel.Instance.RefreshTeamScore(1, 0);
            }

            this.scheduleOnce(() => {
                ResourceUtil.LoadPrefab(`Supplies`).then((prefab: cc.Prefab) => {
                    let node = cc.instantiate(prefab);
                    node.setParent(GameManager.Instance.ItemsNd);
                    NodeUtil.SetWorldPosition(node, NodeUtil.GetWorldPosition(this.node));
                    node.getComponent(Supplies).InitDieBox(this.playerData.Name, items);

                    if (this.isTeamMode) {
                        this.isDie = false;
                        this.StopChasing();
                        if (GameManager.Instance.timer >= 120) {
                            this.MAXHP = 200;
                        } else {
                            this.MAXHP = 60;
                        }
                        this.HP = this.MAXHP;
                        this.playerData = DataManager.GeneratePlayerData();
                        this.RefreshEquip();
                        NodeUtil.SetWorldPosition(this.node, this.oriPosition);
                        this.PlayAni("Idle");
                    } else {
                        this.node.destroy();
                    }
                });
            }, 1);
            value = 0;
        }

        this._hp = ZTool.Clamp(value, 0, this.MAXHP);
        this.hpBar?.Set(this._hp / this.MAXHP);
    }

    rigidbody: cc.RigidBody = null;
    collider: cc.PhysicsCircleCollider = null;

    animation: cc.Animation = null;
    fireAni: cc.Animation = null;
    GunAnimation: cc.Animation = null;

    FirePos: cc.Node;
    Gun: cc.Node;
    Cat: cc.Node;
    GunSp: cc.Sprite;
    Equip: cc.Node;
    Equip_Helmet: cc.Sprite;
    Equip_Bulletproof: cc.Sprite;

    speed: number = 200;
    x: number = 0;
    y: number = 0;

    hpBar: HPBar = null;
    results = null;

    isDie: boolean = false;
    isFire: boolean = false;
    isCooldown: boolean = true;//射击冷却
    isReload: boolean = false;//换弹
    oriPosition: cc.Vec2 = cc.v2();
    targetPosition: cc.Vec2 = cc.v2();

    state: string = "Idle";

    playerData: PlayerData = null;

    gun = null;

    @property()
    isTeamMode: boolean = false;

    @property({ visible() { return this.isTeamMode } })
    isRedTeam: boolean = false;

    onLoad() {
        // this.node.active = Math.random() > 0.3;

        this.rigidbody = this.node.getComponent(cc.RigidBody);
        this.collider = this.node.getComponent(cc.PhysicsCircleCollider);
        this.Cat = NodeUtil.GetNode("Cat", this.node);
        this.Gun = this.node.getChildByName("Gun");
        this.GunSp = NodeUtil.GetComponent("GunSp", this.node, cc.Sprite);
        this.FirePos = NodeUtil.GetNode("FirePos", this.node);
        this.animation = NodeUtil.GetComponent("Cat", this.node, cc.Animation);
        this.Equip = NodeUtil.GetNode("Equip", this.node);
        this.Equip_Helmet = NodeUtil.GetComponent("Equip_Helmet", this.node, cc.Sprite);
        this.Equip_Bulletproof = NodeUtil.GetComponent("Equip_Bulletproof", this.node, cc.Sprite);
        this.fireAni = this.FirePos.getComponent(cc.Animation);
        this.GunAnimation = this.GunSp.getComponent(cc.Animation);

        this.GunAnimation.on('finished', () => {
            if (this.gun) this.gun.MagazineBulletCount = this.gun.MaxMagazineBulletCount;
            this.isReload = false;
            this.isFire = false;
            this.Fire();
        }, this);

        this.playerData = DataManager.GeneratePlayerData();

        PoolManager.GetNode("Prefabs/UI/HPBar", this.node).then(node => {
            node.setPosition(0, this.node.getContentSize().height / 2 + 20);
            this.hpBar = node.getComponent(HPBar);
            this.hpBar.Init(this.isTeamMode ? (this.isRedTeam ? cc.Color.RED : cc.Color.BLUE) : cc.Color.RED);
            if (GameManager.Instance.timer >= 60) {
                this.MAXHP = 200;
            } else {
                this.MAXHP = 60;
            }

            this.HP = this.MAXHP;
        });

        this.oriPosition.set(NodeUtil.GetWorldPosition(this.node));
        this.targetPosition.set(NodeUtil.GetWorldPosition(this.node));
    }

    protected start(): void {
        this.RefreshEquip();
    }

    RefreshEquip() {
        this.Equip_Helmet.node.active = this.playerData.Helmet != null;
        if (this.playerData.Helmet != null) {
            BundleManager.LoadSpriteFrame(Bundles.Item, `${this.playerData.Helmet.Type}_${this.playerData.Helmet.Name}`).then((sf: cc.SpriteFrame) => {
                this.Equip_Helmet.spriteFrame = sf;
                let position = cc.v2((this.playerData.Helmet as 头盔).Position.x, (this.playerData.Helmet as 头盔).Position.y);
                this.Equip_Helmet.node.setPosition(position.multiplyScalar(0.4));
            });
        }

        this.Equip_Bulletproof.node.active = this.playerData.Bulletproof != null;
        if (this.playerData.Bulletproof != null) {
            BundleManager.LoadSpriteFrame(Bundles.Item, `${this.playerData.Bulletproof.Type}_${this.playerData.Bulletproof.Name}`).then((sf: cc.SpriteFrame) => {
                this.Equip_Bulletproof.spriteFrame = sf;
                let position = cc.v2((this.playerData.Bulletproof as 防弹衣).Position.x, (this.playerData.Bulletproof as 防弹衣).Position.y);
                this.Equip_Bulletproof.node.setPosition(position.multiplyScalar(0.4));
            });
        }

        let gunData = null;

        if (this.playerData.Weapon_0 != null) {
            gunData = this.playerData.Weapon_0;
        } else if (this.playerData.Weapon_1 != null) {
            gunData = this.playerData.Weapon_1;
        } else if (this.playerData.Pistol != null) {
            gunData = this.playerData.Pistol;
        }

        if (gunData) {
            BundleManager.LoadSpriteFrame(Bundles.Item, `${gunData.Type}_${gunData.Name}`).then((sf: cc.SpriteFrame) => {
                this.GunSp.spriteFrame = sf;
            });

            this.GunSp.node.setScale(gunData.Type == ItemType[ItemType.手枪] ? 0.4 : 1);
            this.FirePos.setPosition(gunData.FirePosition);

            this.gun = gunData;
        }

    }

    Move(dir: cc.Vec2) {
        if (this.isDie) return;

        this.rigidbody.linearVelocity = dir.multiplyScalar(this.speed);
        this.PlayAni(AniState.Move);
    }

    StopMove() {
        if (this.isDie) return;
        this.rigidbody.linearVelocity = cc.Vec2.ZERO;
        this.PlayAni(AniState.Idle);
    }

    SetGunDir(dir: cc.Vec3) {
        if (this.isDie) return;

        if (this.isFire) {
            this.Cat.scaleX = dir.x < 0 ? -1 : 1;
        }

        let angleRadians = Math.atan2(dir.y, dir.x);
        let angleDegrees = cc.misc.radiansToDegrees(angleRadians);

        this.Gun.scaleX = (angleDegrees > 90 && angleDegrees <= 180 || angleDegrees < -90 && angleDegrees >= -180) ? -1 : 1;
        this.Gun.angle = (angleDegrees > 90 && angleDegrees <= 180 || angleDegrees < -90 && angleDegrees >= -180) ? angleDegrees - 180 : angleDegrees;
    }

    Fire() {
        if (this.isDie) return;
        if (this.isFire) return;

        this.isFire = true;

        if (!this.gun) return;
        if (this.isReload) return;

        if (this.isCooldown) {
            this.isCooldown = false;
            this.StartFire();
        }

        this.schedule(this.StartFire, this.gun.FireRate);
    }

    StartFire() {
        if (this.isDie) return;

        if (!this.gun) return;

        if (this.gun.MagazineBulletCount <= 0) {
            this.StopFire();
            this.Reload();
            return;
        }

        this.fireAni.play();

        PoolManager.GetNode("Prefabs/BulletBlank", this.node).then(node => {
            NodeUtil.SetWorldPosition(node, NodeUtil.GetWorldPosition(this.FirePos));
            node.getComponent(Bullet).InitBlank(this.node, cc.v2(-5 * this.Gun.scaleX, ZTool.GetRandom(5, 8)).multiplyScalar(5), NodeUtil.GetWorldPosition(node).add(cc.v2(ZTool.GetRandom(0.1, 0.5), ZTool.GetRandom(0.1, 0.5))));
        });

        let bulletCount = this.gun.Type == ItemType[ItemType.霰弹枪] ? 5 : 1;

        AudioManager.PlayOneShot(Constant.Audio.Fire);

        for (let i = 0; i < bulletCount; i++) {
            PoolManager.GetNode("Prefabs/Bullet", this.node).then(node => {
                NodeUtil.SetWorldPosition(node, NodeUtil.GetWorldPosition(this.FirePos));
                let recoil = ZTool.GetRandom(this.gun.Recoil * -10 * bulletCount, this.gun.Recoil * 10 * bulletCount);
                node.angle = this.Gun.scaleX < 0 ? this.Gun.angle + recoil - 180 : this.Gun.angle + recoil;
                node.getComponent(Bullet).Init(this.node, this.gun, this.playerData);
                this.gun.MagazineBulletCount -= 1;
                EventManager.Scene.emit(MyEvent.RefreshBulletCount);
            });
        }
    }

    StopFire() {
        if (!this.isFire) return;
        this.isFire = false;
        this.unschedule(this.StartFire);
    }

    Reload() {
        if (this.isDie) return;

        if (this.gun) {
            let clip = this.GunAnimation.getClips().find(e => e.name == this.gun.Name);
            if (clip) {
                this.isReload = true;
                this.GunAnimation.play(clip.name);
            }
        }
    }

    TakeDamage(bullet: Bullet) {
        if (this.isDie) return;
        if (!ZTool.IsStringNullOrEmpty(bullet.playerData.Name)) return;

        let damage = bullet.weapon.Damage;

        let data, lv = 0;
        if (this.playerData.Bulletproof != null) {
            data = this.playerData.Bulletproof;
            lv += data.Lv;
            data.Durability -= damage / 50;
            if (data.Durability <= 0) {
                this.playerData.Bulletproof = null;
            }
        }

        if (this.playerData.Helmet != null) {
            data = this.playerData.Helmet;
            lv += data.Lv;
            data.Durability -= damage / 50;
            if (data.Durability <= 0) {
                this.playerData.Helmet = null;
            }
        }
        console.error(this.HP);
        this.HP -= lv == 0 ? damage : damage / lv;
        console.error(this.MAXHP, this.HP, lv, lv == 0 ? damage : damage / lv);
    }

    TakeMeleeDamage(damage: number) {
        if (this.isDie) return;

        if (DataManager.PlayerData.Bulletproof != null) {
            damage = damage * (DataManager.PlayerData.Bulletproof as any).Lv / 10;
        }

        if (DataManager.PlayerData.Helmet != null) {
            damage = damage * (DataManager.PlayerData.Helmet as any).Lv / 5;
        }

        this.HP -= damage;
    }

    target: cc.Node = null;

    protected update(dt: number): void {
        if (GameManager.IsGameOver) return;

        if (this.isDie) {
            this.rigidbody.linearVelocity = cc.Vec2.ZERO;
            return;
        }

        if (!GameManager.Instance.player) return;

        if (NodeUtil.GetWorldPosition(GameManager.Instance.player).subtract(NodeUtil.GetWorldPosition(this.node)).len() > 3000) {
            this.StopFire();
            // this.StopChasing();
            this.StopMove();
            return;
        }

        this.results = cc.director.getPhysicsManager().rayCast(NodeUtil.GetWorldPosition(this.node), NodeUtil.GetWorldPosition(GameManager.Instance.player), cc.RayCastType.Closest);

        if (this.results && this.results.length >= 1 && this.results[0].collider.node.group == Constant.Group.Player) {
            this.targetPosition.set(NodeUtil.GetWorldPosition(this.results[0].collider.node));
            let dir = this.targetPosition.clone().subtract(NodeUtil.GetWorldPosition(this.node));
            this.Move(cc.v2(dir.normalize()));

            if (dir.len() < 1200) {
                if (dir.len() < 600) {
                    this.StopMove();
                } else {
                    this.Move(cc.v2(dir.normalize()));
                }
                this.SetGunDir(cc.v3(dir.normalize()));
                this.Fire();
            } else {
                this.StopFire();
                this.Move(cc.v2(dir.normalize()));
            }

            // for (let i = 0; i < this.results.length; i++) {
            //     if (this.results[i].collider.node.group == Constant.Group.Obstacle) {
            //         return;
            //     }

            //     if (this.results[i].collider.node.group == Constant.Group.Player) {
            //         let dir = NodeUtil.GetWorldPosition(this.results[i].collider.node).subtract(NodeUtil.GetWorldPosition(this.node));
            //         if (dir.len() < 1000) {
            //             this.StopChasing();
            //             this.SetGunDir(cc.v3(dir.normalize()));
            //             this.Fire();
            //         } else {
            //             this.StopFire();
            //             this.StartChasing();
            //         }
            //         return;
            //     }
            // }

        } else {
            let dir = this.targetPosition.clone().subtract(NodeUtil.GetWorldPosition(this.node));
            if (dir.len() > 1) {
                this.SetGunDir(cc.v3(dir.normalize()));
                this.Move(cc.v2(dir.normalize()));
            } else {
                this.StopFire();
                // this.StopChasing();
                this.StopMove();
            }
        }
    }

    PlayAni(state: string) {
        if (this.state == state) return;
        this.animation.play(state);
        this.state = state;
    }

    //#region 寻路

    redDots: cc.Node[] = [];
    lastNode: AStarNode = null;

    isChasing: boolean = false;

    StartChasing() {
        if (this.isChasing) return;
        this.isChasing = true;
        this.ChasingFunc();
        this.schedule(this.ChasingFunc, 0.5);
    }

    StopChasing() {
        if (!this.isChasing) return;
        this.isChasing = false;
        cc.Tween.stopAllByTarget(this.node)
        this.unschedule(this.ChasingFunc);
    }

    ChasingFunc() {
        if (!GameManager.Instance.player) {
            this.StopChasing();
            return;
        }

        if (GameManager.Instance.IsInCantArrivePosition(GameManager.Instance.player)) return;

        let start = GameManager.Instance.GetMapPosition(this.node);
        let end = GameManager.Instance.GetMapPosition(GameManager.Instance.player);
        if (start && end && start.length !== 0 && start.length !== 0) {
            let path = AStarManager.Instance.FindPath(cc.v2(Number(start.split(`_`)[0]), Number(start.split(`_`)[1])), cc.v2(Number(end.split(`_`)[0]), Number(end.split(`_`)[1])));
            this.Chasing(this.node, path);

            // //寻路点
            // this.redDots.forEach(e => PoolManager.PutNode(e));
            // this.redDots = [];
            // if (!path) return;
            // for (let i = 0; i < path.length; i++) {
            //     const e = path[i];
            //     if (GameManager.Instance.map.has(`${e.x}_${e.y}`)) {
            //         PoolManager.GetNode("Prefabs/UI/RedDot", cc.director.getScene()).then(node => {
            //             node.getComponent(RedDot).Init(i);
            //             let position = NodeUtil.GetWorldPosition(GameManager.Instance.map.get(`${e.x}_${e.y}`));
            //             NodeUtil.SetWorldPosition(node, position);
            //             this.redDots.push(node);
            //         })
            //     }
            // }
        }
    }

    Chasing(node: cc.Node, path: AStarNode[]) {
        if (!path) return;
        let index = path.some(e => e == this.lastNode) ? 1 : 0;
        for (let i = index; i < path.length; i++) {
            const e = path[i];
            if (GameManager.Instance.map.has(`${e.x}_${e.y}`)) {
                cc.Tween.stopAllByTarget(node);
                this.lastNode = e;
                let target = GameManager.Instance.map.get(`${e.x}_${e.y}`);
                let position = node.parent.convertToNodeSpaceAR(NodeUtil.GetWorldPosition(target));
                let time = cc.Vec3.distance(node.position, cc.v3(position.x, position.y)) / this.speed;
                cc.tween(node).to(time, { position: cc.v3(position.x, position.y) }).call(() => {
                    path.splice(0, path.indexOf(e) + 1)
                    this.Chasing(node, path);
                }).start();
                break;
            }
        }
    }

    //#endregion

    //监听
    onBeginContact(contact, selfCollider, otherCollider: cc.Collider) {
        if (otherCollider.node.group == "Bullet") {
            let bullet = otherCollider.node.getComponent(Bullet);
            if (bullet && bullet.weapon) {
                this.TakeDamage(bullet);
            }
            PoolManager.PutNode(otherCollider.node);
        }
    }

    onEndContact(contact, selfCollider, otherCollider) { }
}
